/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System System Specific Module                         */
/*                                                                           */
/*    Copyright (C) 2013 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------   */
/*    HTTP request/response handling                                         */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#include <krnlAsciiCodes.h>
#include <netHTTP.h>

///////////////////////////////////////////////////////////////////////////////
static dosConstString l_http_line_end = "\r\n";
static dosConstString l_http_version_string = "HTTP/1.1";

///////////////////////////////////////////////////////////////////////////////
// Convert full URL address to URL, Host pair
void netHTTPURLToHost(netHTTPRequestInfo* inout_request)
{
	dosStringLength pos;
	dosStringLength i;;

	// determine host
	pos = 0;
	while(inout_request->URL[pos] != '\0')
	{
		if(inout_request->URL[pos] == '/')
		{
			i = pos;
			while(inout_request->URL[i] != '\0')
			{
				inout_request->URL[i-pos] = inout_request->URL[i];
				i++;
			}
			inout_request->URL[i-pos] = '\0';
			break;
		}
		inout_request->Host[pos] = inout_request->URL[pos];
		pos++;
	}
	inout_request->Host[pos]  = '\0';
}

///////////////////////////////////////////////////////////////////////////////
// Creates HTTP header string from header struct
dosStringLength netCreateHTTPHeader(dosString out_buffer, dosStringLength in_buffer_length, netHTTPRequestInfo* in_request)
{
	dosStringLength pos;

	// generate HTTP header
	pos = 0;

	// add http header line
	switch(in_request->Request)
	{
		case HTTPRQ_Put:
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, "PUT ");
			break;

		case HTTPRQ_Get:
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, "GET ");
			break;

		case HTTPRQ_Post:
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, "POST ");
			break;
	}

	pos = strCopyString(out_buffer, in_buffer_length, pos, in_request->URL);
	pos = strCopyCharacter(out_buffer, in_buffer_length, pos, ' ');
	pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_version_string);
	pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );

	// add request content type
	switch(in_request->RequestBodyType)
	{
		case HTTPRBT_JSON:
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, "Content-Type: application/jsonrequest");
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );
			break;

		case HTTPRBT_Binary:
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, "Content-Type: application/octet-stream");
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );
			break;
	}

	// add accept content type
	switch(in_request->AcceptBodyType)
	{
		case HTTPABT_JSON:
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, "Accept: application/jsonrequest");
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );
			break;

		case HTTPRBT_Binary:
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, "Accept: application/octet-stream");
			pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );
			break;
	}

	// add content length
	if(in_request->ContentLength != DOS_NULL)
	{
		pos = strCopyConstString(out_buffer, in_buffer_length, pos, "Content-Length: ");	
		pos = strDWordToStringPos(out_buffer, in_buffer_length, pos, *in_request->ContentLength, 0, 0, 0);
		pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );
	}

	// add content crc
	if(in_request->CRC != DOS_NULL)
	{
		pos = strCopyConstString(out_buffer, in_buffer_length, pos, "Content-CRC: ");	
		pos = strDWordToHexStringPos(out_buffer, in_buffer_length, pos, *in_request->CRC);
		pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );
	}

	// add host 
	if(in_request->Host[0] != '\0')
	{
		pos = strCopyConstString(out_buffer, in_buffer_length, pos, "Host: ");
		pos = strCopyString(out_buffer, in_buffer_length, pos, in_request->Host);
		pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );
	}

	// header end
	pos = strCopyConstString(out_buffer, in_buffer_length, pos, l_http_line_end );

	return pos;
}

///////////////////////////////////////////////////////////////////////////////
// Parses HTTP response header
dosBool netParseHTTPHeader(dosString in_buffer, dosStringLength in_buffer_length, dosWord* out_status_code, dosConstString in_expected_header_keys[], dosString out_header_values[], dosString in_value_string_buffer, dosStringLength in_value_buffer_length)
{
	dosStringLength pos;
	dosBool success;
	int token_index;
	dosStringLength value_pos;
	dosBool status_parsed;

	// Init header values
	token_index = 0;
	while(in_expected_header_keys[token_index] != DOS_NULL)
	{
		out_header_values[token_index++] = DOS_NULL;
	}

	// parse status line
	success = dosTrue;
	pos = 0;
	strSkipWhitespaces(in_buffer, in_buffer_length, &pos);

	strCheckForConstStringNoCase(in_buffer, in_buffer_length, &pos, &success, l_http_version_string);
	strSkipWhitespaces(in_buffer, in_buffer_length, &pos);
	strStringToWord(in_buffer, in_buffer_length, &pos, &success, out_status_code);
	strSkipLineEnd(in_buffer, in_buffer_length, &pos, dosString_LE_CRLF);
	status_parsed = success;

	// parse header values
	value_pos = 0;
	while(pos < in_buffer_length - 1 && in_buffer[pos] != ASCII_CR && in_buffer[pos+1] != ASCII_LF)
	{
		// check key values
		strSkipWhitespaces(in_buffer, in_buffer_length, &pos);
		success = dosTrue;
		token_index =	strCheckForTokenNoCase(in_buffer, in_buffer_length, &pos, &success, in_expected_header_keys);
		if(success && token_index >= 0)
		{
			strSkipWhitespaces(in_buffer, in_buffer_length, &pos);
			strCheckForSeparator(in_buffer, in_buffer_length, &pos, &success, ':');
			strSkipWhitespaces(in_buffer, in_buffer_length, &pos);

			// store value
			out_header_values[token_index] = (dosString)(&in_value_string_buffer[value_pos]);
			while(success && value_pos < in_value_buffer_length - 1 && pos < in_buffer_length - 1 && in_buffer[pos] != ASCII_CR && in_buffer[pos+1] != ASCII_LF)
			{
				in_value_string_buffer[value_pos++] = in_buffer[pos++];
			}
			in_value_string_buffer[value_pos++] = '\0';

			strSkipLineEnd(in_buffer, in_buffer_length, &pos, dosString_LE_CRLF);
		}
		else
		{
			// skip this line
			strSkipLineEnd(in_buffer, in_buffer_length, &pos, dosString_LE_CRLF);
		}
	}

	return status_parsed;
}